const ONE_DAY_TIMESTAMP = 86400000;    // 一天毫秒数
const unitList = [
  'year', 'y',
  'month', 'M',
  'date', 'd',
  'hour', 'H',
  'minute', 'm',
  'second', 's',
  'millisecond', 'ms'
];
// 农历日期处理数据和方法
const calendar = {
  // 农历1900-2100的闰大小信息表
  lunarInfo: [
    0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2,
    0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977,
    0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970,
    0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950,
    0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557,
    0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0,
    0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0,
    0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6,
    0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570,
    0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0,
    0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5,
    0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930,
    0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530,
    0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45,
    0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0,
    0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0,
    0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4,
    0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0,
    0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160,
    0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252,
    0x0d520
  ],
  solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], // 公历每月天数
  tianGan: ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'], // 10天干
  diZhi: ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'], // 12地支
  monthDiZhi: ['寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥', '子', '丑'], // 月分对应12地支
  yearCN: ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'], // 年对应汉字
  Animals: ['鼠', '牛', '虎', '兔', '龙', '蛇', '马', '羊', '猴', '鸡', '狗', '猪'], // 12生肖
  monthCN: ['正月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '冬月', '腊月'], // 对应的月份
  dayCN: ['初一', '初二', '初三', '初四', '初五', '初六', '初七', '初八', '初九', '初十', '十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八', '十九', '廿二', '廿十一', '廿十二', '廿十三', '廿十四', '廿十五', '廿十六', '廿十七', '廿十八', '廿十九', '三十'],

  /**
   * 返回农历y年一整年的总天数
   * @param y
   */
  lunarYearDays: function (y) {
    let i, sum = 348;

    for (i = 0x8000; i > 0x8; i >>= 1) {
      sum += calendar.lunarInfo[y - 1900] & i ? 1 : 0;
    }

    return sum + calendar.leapDays(y);
  },

  /**
   * 返回农历年润月份,无返回0
   * @param { Number } y 农历年
   */
  leapMonth: function (y) {
    return calendar.lunarInfo[y - 1900] & 0xf;
  },

  /**
   * 返回农历y年闰月的天数, 无返回0
   * @param { Number } y 农历年
   */
  leapDays: function (y) {
    if (calendar.leapMonth(y)) {
      return calendar.lunarInfo[y - 1900] & 0x10000 ? 30 : 29;
    }

    return 0;
  },

  /**
   * 返回农历年月(非闰月)的总天数，计算month为闰月天数使用leapDays方法
   * @param { Number } y 年
   * @param { Number } m 月
   */
  monthDays: (y, m) => {
    return calendar.lunarInfo[y - 1900] & 0x10000 >> m ? 30 : 29;
  },

  /**
   * 农历年份转换为干支纪年
   * @param  lYear 农历年的年份数
   */
  getGanZhiYear: function (lYear) {
    let ganKey = (lYear - 3) % 10;
    let zhiKey = (lYear - 3) % 12;

    if (ganKey === 0) {
      ganKey = 10; // 如果余数为0则为最后一个天干
    }
    if (zhiKey === 0) {
      zhiKey = 12; // 如果余数为0则为最后一个地支
    }

    return calendar.tianGan[ganKey - 1] + calendar.diZhi[zhiKey - 1];
  },

  /**
   * @param { Number } lYear 农历年
   * @param { Number } lMonth 农历月
   */
  getGanZhiMonth: function (lYear, lMonth) {
    let lYearGanNumber = (lYear - 3) % 10;

    if (lYearGanNumber === 0) {
      lYearGanNumber = 10; // 如果余数为0则为最后一个天干
    }
    const monthTianGan = lYearGanNumber * 2 + lMonth;
    const newMonthTianGan = monthTianGan % 10;

    return calendar.tianGan[newMonthTianGan - 1] + calendar.monthDiZhi[lMonth - 1];
    // 月天干＝年份天干对应数字×2＋农历月份，得出的数字如果是双数，就减十，直到减为单数为止；月地支不用算，农历月份对应的地支就是月地支
  },

  /**
   * @param { Number } offsetDay
   */
  getGanZhiDay: (offsetDay) => {
    return calendar.tianGan[offsetDay % 10] + calendar.diZhi[offsetDay % 12];
  },

  /**
   * @param { String } date  日期格式2022/02/22
   * @returns { Object }
   */
  lunarDate: (date) => {
    const newData = new Date(date);
    const solarYear = newData.getFullYear();
    const solarMonth = newData.getMonth() + 1;
    const solarDay = newData.getDate();

    // 年份限定、上限
    if (solarYear < 1901 || solarYear > 2099) {
      return '仅支持1901-2099年日期查询,传入的时间超出范围';
    }
    // Date.UTC 返回数值，表示指定日期时间与 1970 年 1 月 1 日午夜之间的毫秒数
    let offset = (Date.UTC(solarYear, solarMonth - 1, solarDay) - Date.UTC(1900, 0, 31)) / 86400000;
    let lunarCurrYear, leap = 0, temp = 0;

    for (lunarCurrYear = 1900; lunarCurrYear < 2101 && offset > 0; lunarCurrYear++) {
      temp = calendar.lunarYearDays(lunarCurrYear);
      offset -= temp;
    }
    if (offset < 0) {
      offset += temp;
      lunarCurrYear--;
    }

    const lunarYear = lunarCurrYear; // 农历年

    leap = calendar.leapMonth(lunarCurrYear); // 闰哪个月
    let isLeap = false; // 是否闰月
    let lunarCurrMonth;

    // 效验闰月
    for (lunarCurrMonth = 1; lunarCurrMonth < 13 && offset > 0; lunarCurrMonth++) {
      // 闰月
      if (leap > 0 && lunarCurrMonth === leap + 1 && !isLeap) {
        --lunarCurrMonth;
        isLeap = true;
        temp = calendar.leapDays(lunarYear); // 计算农历闰月天数
      } else {
        temp = calendar.monthDays(lunarYear, lunarCurrMonth); // 计算农历普通月天数
      }
      // 解除闰月
      if (isLeap && lunarCurrMonth === leap + 1) {
        isLeap = false;
      }
      offset -= temp;
    }
    // 闰月导致数组下标重叠取反
    if (offset === 0 && leap > 0 && lunarCurrMonth === leap + 1) {
      if (isLeap) {
        isLeap = false;
      } else {
        isLeap = true;
        --lunarCurrMonth;
      }
    }
    if (offset < 0) {
      offset += temp;
      --lunarCurrMonth;
    }

    const lunarMonth = lunarCurrMonth; // 农历月
    const lunarDay = offset + 1; // 农历日

    // 其他数据格式化
    // 汉字年份
    const arrYearDataStr = lunarYear.toString().split('');
    const lunarYearCN = arrYearDataStr.reduce(function (total, item) {
      total += calendar.yearCN[Number(item)];

      return total;
    }, '');
    const lunarMonthCN = calendar.monthCN[lunarMonth - 1]; // 汉字月份
    const lunarDayCN = calendar.dayCN[lunarDay - 1]; // 汉字日
    const ganZhiYear = calendar.getGanZhiYear(lunarYear); // 干支年
    const ganZhiMonth = calendar.getGanZhiMonth(lunarYear, lunarMonth);
    // 日柱 当月一日与 1900/1/1 相差天数
    const dayCyclical = Date.UTC(solarYear, solarMonth - 1, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10;
    const ganZhiDay = calendar.getGanZhiDay(dayCyclical + solarDay - 1);
    const zodiac = calendar.Animals[(lunarYear - 4) % 12]; // 生肖

    return {
      lunarYear: lunarYear,
      lunarMonth: lunarMonth,
      lunarDay: lunarDay,
      lunarYearCN: lunarYearCN,
      lunarMonthCN: isLeap ? '润' + lunarMonthCN : lunarMonthCN,
      lunarLeapMonth: leap,
      lunarIsLeapMonth: isLeap,
      lunarDayCN: lunarDayCN,
      lunarZodiac: zodiac,
      ganZhiYear: ganZhiYear,
      ganZhiMonth: ganZhiMonth,
      ganZhiDay: ganZhiDay
    };
  }
};

class Wdate {

  /**
   * @param { String | Number } date // 时间 { milliseconds | dateString }
   */
  constructor(date) {
    this.hdDate = '';
    // 未传参数取当前系统日期
    this.errStatus = false;
    if (arguments.length === 0) {
      this.hdDate = new Date();
    } else {
      if (this.isValidDate(date)) {
        this.hdDate = this.getParamDate(date);
      } else {
        this.message = '传入非法参数';
        this.errStatus = true;
      }
    }
  }

  /**
   *  @desc 通用参数说明
   *  @param { Boolean } isFillZero 是否补0
   */

  /*
   * 1、常规方法
   **/

  // 返回年份
  getYear(isFillZero = false) {
    const year = this.hdDate.getFullYear();

    return this.isParamsError(isFillZero ? String(year) : year);
  }

  // 返回月份(从1-12)
  getMonth(isFillZero = false) {
    const month = this.hdDate.getMonth() + 1;

    return this.isParamsError(this.getFillZero(month, isFillZero));
  }

  // 返回月中的第几天(从1-31)
  getDate(isFillZero = false) {
    const day = this.hdDate.getDate();

    return this.isParamsError(this.getFillZero(day, isFillZero));
  }

  /**
   *  @desc 返回小时(从 0-23/  1-12)
   *  @param { Boolean } isFillZero 是否补0
   *  @param { isTwentyFour } isTwentyFour 是否是24小时制
   */
  getHours(isFillZero = false, isTwentyFour = true) {
    const hour = this.hdDate.getHours();

    return this.isParamsError(this.getFillZero(!isTwentyFour && hour > 12 ? hour % 12 : hour, isFillZero));
  }

  // 返回分钟(从 0-59) | 可补0
  getMinutes(isFillZero = false) {
    const minutes = this.hdDate.getMinutes();

    return this.isParamsError(this.getFillZero(minutes, isFillZero));
  }

  // 返回秒(从 0-59) | 可补0
  getSeconds(isFillZero = false) {
    const seconds = this.hdDate.getSeconds();

    return this.isParamsError(this.getFillZero(seconds, isFillZero));
  }

  // 返回毫秒(从 0-999) | 可补0
  getMilliseconds(isFillZero = false) {
    const milliseconds = this.hdDate.getMilliseconds();

    return this.isParamsError(this.getFillZero(milliseconds, isFillZero));
  }

  /**
   * @desc 日期相加
   * @param { String } unit 单位
   * @param { Boolean } isFillZero 是否补0
   */
  get(unit = 'date', isFillZero = false) {
    let resData = '';

    if (!unitList.includes(unit)) {
      throw new Error('unit参数错误');
    }

    switch (unit) {
    case 'year':
    case 'y':
      resData = this.getYear(isFillZero);
      break;
    case 'month':
    case 'M':
      resData = this.getMonth(isFillZero);
      break;
    case 'date':
    case 'd':
      resData = this.getDate(isFillZero);
      break;
    case 'hour':
    case 'H':
      resData = this.getHours(isFillZero);
      break;
    case 'minute':
    case 'm':
      resData = this.getMinutes(isFillZero);
      break;
    case 'second':
    case 's':
      resData = this.getSeconds(isFillZero);
      break;
    case 'millisecond':
    case 'ms':
      resData = this.getMilliseconds(isFillZero);
      break;
    default:
      return;
    }

    return resData;
  }

  /**
   *  @desc 返回星期几(从0-6) | 0-代表星期日
   *  @param { Boolean } isChinese 是否显示中文
   */
  getDay(isChinese = true) {
    const weekIndex = this.hdDate.getDay();
    const textList = ['日', '一', '二', '三', '四', '五', '六'];
    const resData = isChinese ? textList[weekIndex] : weekIndex;

    return this.isParamsError(resData);
  }

  /**
   *  @desc 返回时间戳 | 10 秒 13 毫秒
   *  @param { Number } length 长度
   */
  getTime(length = 13) {
    let timestamp = '';

    if (length === 13) {
      timestamp = this.hdDate.getTime();
    } else if (length === 10) {
      timestamp = parseInt(this.hdDate.getTime() / 1000);
    } else {
      return '传入参数非法';
    }

    return this.isParamsError(timestamp);
  }

  /*
   * 2、扩展方法
   **/
  // 获取日期当月总天数
  getMonthCountDay() {
    // 设置当前日期下一月的时间
    const nextMouth = `${this.getYear()}/${this.getMonth() + 1}/01 00:00:01`;
    const currMouth = new Date(nextMouth).getTime() - 2000; // 当月最后一天，是多少号，总天数就是多少
    const resData = new Date(currMouth).getDate();

    return this.isParamsError(resData);
  }

  /**
   * @desc 获取当前日期是在什么季度
   * @param {*} forMartText 格式化结果
   */
  getQuarter(forMartText) {
    const currMounth = this.hdDate.getMonth() + 1;
    let resData = '';

    if (currMounth <= 3) {
      resData = 1;
    } else if (currMounth <= 6) {
      resData = 2;
    } else if (currMounth <= 9) {
      resData = 3;
    } else {
      resData = 4;
    }
    if (forMartText instanceof Array && forMartText && forMartText.length === 4) {
      return this.isParamsError(forMartText[resData - 1]);
    }

    return this.isParamsError(resData);
  }

  // 返回年龄
  getAge() {
    const localYear = new Date().getFullYear(),
      localMonth = new Date().getMonth() + 1,
      localDay = new Date().getDate(),
      paramYear = this.getYear(),
      paramMonth = this.getMonth(),
      paramDay = this.getDate();
    let age = 0;

    if (this.getTime() > new Date().getTime()) {
      return '传入日期不能在当前日期之后';
    } else {
      if (localYear === paramYear) {
        return age;
      }
      if (localYear > paramYear) {
        age = localYear - paramYear - 1;
        if (localMonth > paramMonth) {
          age++;
        } else if (localMonth == paramMonth) {
          if (localDay >= paramDay) {
            age++;
          }
        }
      }
    }

    return this.isParamsError(age);
  }

  // 返回星座
  getCconstellation() {
    const xzList = [
      { start: '01/20', end: '02/18', name: '水平座' },
      { start: '02/19', end: '03/20', name: '双鱼座' },
      { start: '03/21', end: '04/19', name: '白羊座' },
      { start: '04/20', end: '05/20', name: '金牛座' },
      { start: '05/21', end: '06/21', name: '双子座' },
      { start: '06/22', end: '07/22', name: '巨蟹座' },
      { start: '07/23', end: '08/22', name: '狮子座' },
      { start: '08/23', end: '09/22', name: '处女座' },
      { start: '09/23', end: '10/23', name: '天秤座' },
      { start: '10/24', end: '11/22', name: '天蝎座' },
      { start: '11/23', end: '12/21', name: '射手座' },
      { start: '12/22', end: '12/31', name: '摩羯座' },
      { start: '01/01', end: '01/19', name: '摩羯座' }
    ];
    let name = '';

    for (let i = 0; i < 13; i++) {
      const currT = this.getAllDate('yy/MM/dd');
      const currTimestamp = new Date(currT).getTime();
      const year = this.getYear();
      const startT = year + '/' + xzList[i].start;
      const endT = year + '/' + xzList[i].end;
      const startTimestamp = new Date(startT).getTime();
      const endTimestamp = new Date(endT).getTime();

      if (currTimestamp >= startTimestamp && currTimestamp <= endTimestamp) {
        name = xzList[i].name;
        break;
      }
    }

    return this.isParamsError(name);
  }

  // 判断是当年的第几天
  getDayOfYear() {
    // 获取当年第一天时间戳
    const localTime = this.getLocalTimeInfo();
    const currStr = `${localTime.yy}/01/01`;
    const fristDayTimestamp = new Date(currStr).getTime();
    const day = parseInt((this.getTime() - fristDayTimestamp) / ONE_DAY_TIMESTAMP);

    if (this.getYear() !== localTime.yy) {
      return '传入日期不是当年日期';
    }

    return this.isParamsError(day + 1);
  }

  /**
   * @desc 日期相加
   * @param { String } unit 单位
   * @param { Number } size 数值
   * @param { String } format 回显格式
   */

  getAdd(size = 0, unit = 'date', format = 'yy-MM-dd HH:mm:ss') {
    size = Number(size);
    const unitListGet = [
      ...unitList,
      'quarter', 'Q',
      'week', 'w'
    ];

    if (!unitListGet.includes(unit)) {
      return this.isParamsError('unit或size参数错误');
    }
    if (isNaN(size) || size % 1 !== 0) {
      return this.isParamsError('size参数错误');
    }
    const currDate = new Date(this.hdDate.getTime());

    switch (unit) {
    case 'year':
    case 'y':
      currDate.setFullYear(this.getYear() + size);
      break;
    case 'M':
    case 'month':
      currDate.setMonth(this.getMonth() + size - 1); // this.getMonth 已经加1， 所以此处应减去1
      break;
    case 'd':
    case 'date':
      currDate.setDate(this.getDate() + size);
      break;
    case 'H':
    case 'hour':
      currDate.setHours(this.getHours() + size);
      break;
    case 'm':
    case 'minute':
      currDate.setMinutes(this.getMinutes() + size);
      break;
    case 's':
    case 'second':
      currDate.setSeconds(this.getSeconds() + size);
      break;
    case 'ms':
    case 'millisecond':
      currDate.setMilliseconds(this.getMilliseconds() + size);
      break;
    case 'Q':
    case 'quarter':
      currDate.setMonth(this.getMonth() + size * 3 - 1); // this.getMonth 已经加1， 所以此处应减去1
      break;
    case 'w':
    case 'week':
      currDate.setDate(this.getDate() + size * 7);
      break;
    default:
      return;
    }
    const newCurrDate = this.getLocalTimeInfo(currDate);
    const resData = new Wdate(newCurrDate.timestamp);

    return this.isParamsError(resData.getFormat(format));
  }

  /**
   * @desc 日期相减
   * @param { String } unit 单位
   * @param { Number } size 数值
   * @param { String } format 回显格式
   */
  getSubtract(size = 0, unit = 'date', format = 'yy-MM-dd HH:mm:ss') {
    return this.isParamsError(this.getAdd(-1 * size, unit, format));
  }

  /**
   * @param { String|Number } dateTime 需要计算的日期
   */
  getDateDiff(dateTime = new Date().getTime()) {
    const isLegal = this.isValidDate(dateTime);

    if (!isLegal) {
      return this.isParamsError('dateTime传入参数错误');
    } else {
      const paramsTime = this.getParamDate(dateTime);
      const subTime = this.getTime() - paramsTime.getTime();

      if (subTime === 0) {
        return this.isParamsError('两个日期相同');
      }
      const resData = this.getFuture(Math.abs(subTime));

      resData.timestamp = subTime; // 返回正负时间戳

      return this.isParamsError(resData);
    }
  }

  /**
   * @param { String|Number } dateTime 需要计算的日期
   */
  getDiffDay(dateTime = new Date().getTime()) {
    const isLegal = this.isValidDate(dateTime);

    if (!isLegal) {
      return this.isParamsError('dateTime传入参数错误');
    } else {
      const paramsTime = this.getParamDate(dateTime);
      const newParamsTime = new Date(`${paramsTime.getFullYear()}/${paramsTime.getMonth() + 1}/${paramsTime.getDate()}`);
      const classTime = new Date(`${this.getYear()}/${this.getMonth()}/${this.getDate()}`);
      const subTime = (classTime.getTime() - newParamsTime.getTime()) / ONE_DAY_TIMESTAMP;

      return this.isParamsError(subTime);
    }
  }

  /*
   * 3、格式数据方法
   **/

  /**
   * @desc 获取时间段，返回所对应的文案
   * @param {*} hour 时间段
   * @param {*} text 文案
   */
  getTimeFrame(text = ['AM', 'PM'], hour = [{ start: 0, end: 12 }, { start: 12, end: 24 }]) {
    if (hour.length != text.length) {
      return '参数错误';
    }
    const currHour = this.getHours();
    let resData = '';

    for (let i = 0; i < hour.length; i++) {
      if (currHour >= hour[i].start && currHour < hour[i].end) {
        resData = text[i];
        break;
      }
    }

    return this.isParamsError(resData);
  }

  /**
   * @desc 返回格式化日期
   * @param { String } format // 显示内容 yy 代表年 MM 代表月 dd 代表日
   * @param { Boolean } isFillZero // 是否补0
   */
  getAllDate(format = 'yy-MM-dd', isFillZero = true) {
    const resData = this.getTimeStr(format, isFillZero, 'Date');

    return this.isParamsError(resData);
  }

  /**
   * @desc 返回格式化时间
   * @param { String } format // 显示内容 HH小时(24小时制) hh小时(12小时制) mm 分钟 ss 秒 ww 星期
   * @param { Boolean } isFillZero // 是否补0
   */
  getAllTime(format = 'HH:mm:ss', isFillZero = true) {
    const resData = this.getTimeStr(format, isFillZero, 'Time');

    return this.isParamsError(resData);
  }

  /**
   * @param {*} format 时间格式
   * @param {*} isFillZero 是否补0
   * @returns yy-MM-dd HH:mm:ss 指定格式时间
   */
  getAllDateTime(format = 'yy-MM-dd HH:mm:ss', isFillZero = true) {
    const resData = this.getTimeStr(format, isFillZero, 'All');

    return this.isParamsError(resData);
  }

  /**
   * @param {*} format 时间格式
   * @param {*} isFillZero 是否补0
   * @returns yy-MM-dd HH:mm:ss 指定格式时间
   */
  getFormat(format = 'yy-MM-dd HH:mm:ss', isFillZero = true) {
    const resData = this.getTimeStr(format, isFillZero, 'All');

    return this.isParamsError(resData);
  }

  // 根据时间范围返回时间【当天】、星期【7天内】、具体日期【7天前】
  getFormatOne(format = {}, isFillZero = true) {
    const paramsObj = {
      sevenIn: 'sevenIn' in format ? format.sevenIn : '星期ww',
      sevenOut: 'sevenOut' in format ? format.sevenOut : 'yy-MM-dd HH:mm:ss',
      today: 'today' in format ? format.today : 'HH:mm:ss'
    };
    const timeDifference = (new Date().getTime() - this.getTime()) / 86400000;

    // 当天
    if (timeDifference < 1) {
      return this.getFormat(paramsObj.today, isFillZero);
    } else if (timeDifference < 7) {
      return this.getFormat(paramsObj.sevenIn, isFillZero);
    } else {
      return this.getFormat(paramsObj.sevenOut, isFillZero);
    }
  }

  /**
   * @desc 倒计时时间展示
   * @param { Number } timestamp  时间戳
   */
  getFuture(timestamp) {
    let newTime = '';

    if (timestamp <= 0) {
      return '传入非法参数';
    }
    if (timestamp) {
      newTime = timestamp;
    } else {
      newTime = Math.abs(this.getTime() - new Date().getTime());
    }

    const resData = {
      day: this.getSubDate(newTime),
      hours: this.getSubHours(newTime) % 24,
      hoursCount: this.getSubHours(newTime),
      minutes: this.getSubMinutes(newTime),
      seconds: this.getSubSeconds(newTime),
      timestamp: newTime
    };

    return this.isParamsError(resData);
  }

  /**
   *  @desc 数字小于10补0
   *  @param { Number } number 传入的数字
   *  @param { Boolean } isFillZero 传入的数字
   */
  getFillZero(number, isFillZero = false) {
    if (isFillZero) {
      if (number < 10) {
        return `0${number}`;
      } else {
        return String(number);
      }
    } else {
      return number;
    }
  }

  /*
   * 4、农历日期方法
   **/

  // 返回生肖
  getLunarZodiac() {
    const lunarData = calendar.lunarDate(this.getAllDate('yy/MM/dd'));

    return this.isParamsError(lunarData.lunarZodiac);
  }

  // 返回农历年月日
  getLunar() {
    const lunarData = calendar.lunarDate(this.getAllDate('yy/MM/dd'));
    const text = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥', '子'],
      hour = [
        { start: 0, end: 1 },
        { start: 1, end: 3 },
        { start: 3, end: 5 },
        { start: 5, end: 7 },
        { start: 7, end: 9 },
        { start: 9, end: 11 },
        { start: 11, end: 13 },
        { start: 13, end: 15 },
        { start: 15, end: 17 },
        { start: 17, end: 19 },
        { start: 19, end: 21 },
        { start: 21, end: 23 },
        { start: 23, end: 24 }
      ];
    const resData = {
      ...lunarData,
      lunarHourCn: this.getTimeFrame(text, hour) + '时'
    };

    return this.isParamsError(resData);
  }

  /*
   * 5、日期判断方法
   **/

  /**
   * @desc 检查日期是否合法 不区分平台
   * @param { Any } date 传入检验的日期
   */
  isValid(date, res = false) {
    const newDete = new Date(date);

    if (newDete instanceof Date && !isNaN(newDete.getTime())) {
      if (res) {
        return {
          date: newDete,
          timestamp: newDete.getTime(),
          res: true
        };
      } else {
        return true;
      }

    } else {
      return false;
    }
  }

  // 判断是否是闰年
  isLeapYear() {
    const year = this.getYear();

    return this.isParamsError(year % 4 == 0 && year % 100 != 0 || year % 400 == 0);
  }

  /**
   * @desc 判断传入日期是否小于指定日期
   * @param { String | Number } compareDate  // 比较的日期
   * @returns { Boolean }
   */
  isBefore(compareDate) {
    const isLegal = this.isValidDate(compareDate);
    const newCompareDate = this.getParamDate(compareDate);

    if (!isLegal) {
      return 'compareDate传入参数非法';
    } else {
      const assignDate = this.hdDate.getTime();
      const compareDate = newCompareDate.getTime();

      return this.isParamsError(compareDate < assignDate);
    }
  }

  /**
   * @desc 判断传入日期是否等于指定日期
   * @param { String | Number } compareDate  // 比较的日期
   * @returns { Boolean }
   */
  isSame(compareDate) {
    const isLegal = this.isValidDate(compareDate);
    const newCompareDate = this.getParamDate(compareDate);

    if (!isLegal) {
      return 'compareDate传入参数非法';
    } else {
      const assignDate = this.hdDate.getTime();
      const compareDate = newCompareDate.getTime();

      return this.isParamsError(compareDate === assignDate);
    }
  }

  /**
   * @desc 判断传入日期是否大于指定日期
   * @param { String | Number } compareDate  // 比较的日期
   * @returns { Boolean }
   */
  isAfter(compareDate) {
    const isLegal = this.isValidDate(compareDate);
    const newCompareDate = this.getParamDate(compareDate);

    if (!isLegal) {
      return 'compareDate传入参数非法';
    } else {
      const assignDate = this.hdDate.getTime();
      const compareDate = newCompareDate.getTime();

      return this.isParamsError(compareDate > assignDate);
    }
  }

  // 判断是否是当年
  isThisYear() {
    const localTime = this.getLocalTimeInfo();
    let resData = false;

    if (this.getYear() === localTime.yy) {
      resData = true;
    }

    return this.isParamsError(resData);
  }

  // 判断是否是当月
  isThisMonth() {
    const localTime = this.getLocalTimeInfo();
    let resData = false;

    if (this.getYear() === localTime.yy && localTime.MM === this.getMonth()) {
      resData = true;
    }

    return this.isParamsError(resData);
  }

  // 判断是否本周
  isThisWeek() {
    const localTime = this.getLocalTimeInfo();
    const currStr = `${localTime.yy}/${localTime.MM}/${localTime.dd}`;
    const currTime = new Date(currStr).getTime();
    let resData = false;

    // 判断是否是当年
    if (this.getYear() === localTime.yy) {
      // timestamp
      const startDay = localTime.week === 0 ? 7 : localTime.week;
      const startT = currTime - (startDay - 1) * ONE_DAY_TIMESTAMP;
      const endT = startT + 7 * ONE_DAY_TIMESTAMP - 1;

      if (this.getTime() >= startT && this.getTime() <= endT) {
        resData = true;
      }
    }

    return this.isParamsError(resData);
  }

  // 判断是否是当天
  isToday() {
    const localTime = this.getLocalTimeInfo();
    let resData = false;

    if (this.getYear() === localTime.yy && localTime.MM === this.getMonth() && localTime.dd === this.getDate()) {
      resData = true;
    }

    return this.isParamsError(resData);
  }

  // 判断是否是工作日
  isWorkDay() {
    let resData = false;

    if (this.getDay(false) != 0 && this.getDay(false) != 6) {
      resData = true;
    }

    return this.isParamsError(resData);
  }

  /**
   * @param {String | Number} startTime 开始时间
   * @param {String | Number} endTime 结束时间
   */
  isBetween(startTime, endTime) {
    if (arguments.length === 0 || arguments.length === 1) {
      return this.isParamsError('startTime和endTime不能为空');
    }
    if (this.isValidDate(startTime) && this.isValidDate(endTime)) {
      const startDate = this.getParamDate(startTime);
      const endDate = this.getParamDate(endTime);

      if (this.getTime() >= startDate.getTime() && this.getTime() <= endDate.getTime()) {
        return this.isParamsError(true);
      } else {
        return this.isParamsError(false);
      }
    } else {
      return this.isParamsError('传入非法参数');
    }
  }

  /*
   * 6、日期转换
   **/

  // 返回包含时间信息的 string
  toString() {
    return this.isParamsError(this.hdDate.toString());
  }

  // 将日期转换数组
  toArray(isFillZero) {
    const resData = [
      this.getYear(isFillZero),
      this.getMonth(isFillZero),
      this.getDate(isFillZero),
      this.getHours(isFillZero),
      this.getMinutes(isFillZero),
      this.getSeconds(isFillZero),
      this.getMilliseconds(isFillZero)
    ];

    return this.isParamsError(resData);
  }

  // 序列化为 ISO 8601 格式的字符串
  toJSON() {
    return this.isParamsError(this.hdDate.toJSON());
  }

  // 返回包含时间信息的 Object
  toObject(isFillZero) {
    const resData = {
      year: this.getYear(isFillZero),
      month: this.getMonth(isFillZero),
      date: this.getDate(isFillZero),
      hours: this.getHours(isFillZero),
      minutes: this.getMinutes(isFillZero),
      seconds: this.getSeconds(isFillZero),
      timestamp: isFillZero ? String(this.getTime()) : this.getTime()
    };

    return this.isParamsError(resData);
  }

  /*
   * 7、日期设置方法
   **/

  /**
   * @desc 设置年份
   * @param { Number } year 数值
   */
  setYear(year) {
    return this.set(year, 'year');
  }

  /**
   * @desc 设置月份
   * @param { Number } month 数值
   */
  setMonth(month) {
    return this.set(month, 'month');
  }

  /**
   * @desc 设置日
   * @param { Number } date 数值
   */
  setDate(date) {
    return this.set(date, 'date');
  }

  /**
   * @desc 设置小时
   * @param { Number } hour 数值
   */
  setHours(hour) {
    return this.set(hour, 'hour');
  }

  /**
   * @desc 设置分钟
   * @param { Number } min 数值
   */
  setMinutes(min) {
    return this.set(min, 'minute');
  }

  /**
   * @desc 设置秒
   * @param { Number } sec 数值
   */
  setSeconds(sec) {
    return this.set(sec, 'second');
  }

  /**
   * @desc 设置毫秒
   * @param { Number } millisec 数值
   */
  setMilliseconds(millisec) {
    return this.set(millisec, 'millisecond');
  }

  /**
   * @desc 设置指定类别值
   * @param { String } unit 单位
   * @param { Number } size 数值
   */
  set(size = 0, unit = 'date') {
    size = Number(size);

    if (!unitList.includes(unit)) {
      throw new Error('unit参数错误');
    }
    if (isNaN(size) || size % 1 !== 0) {
      throw new Error('size参数错误');
    }
    if (unit === 'year') {
      if (isNaN(size) || size < 1970 || size > 2200) {
        throw new Error('年份参数只能在1970-2200年之间');
      }
    }
    switch (unit) {
    case 'y':
    case 'year':
      this.hdDate.setFullYear(size);
      break;
    case 'M':
    case 'month':
      this.hdDate.setMonth(size - 1);
      break;
    case 'd':
    case 'date':
      this.hdDate.setDate(size);
      break;
    case 'H':
    case 'hour':
      this.hdDate.setHours(size);
      break;
    case 'm':
    case 'minute':
      this.hdDate.setMinutes(size);
      break;
    case 's':
    case 'second':
      this.hdDate.setSeconds(size);
      break;
    case 'ms':
    case 'millisecond':
      this.hdDate.setMilliseconds(size);
      break;
    default:
      return;
    }

    return this;
  }

  /**
   * @desc 设置指定类别值
   * @param { String } unit 单位
   */
  setStartOf(unit = 'year') {
    const unitListOf = [
      ...unitList,
      'quarter', 'Q',
      'week', 'w',
      'weekMonday', 'wm'
    ];
    const startValue = 0;
    const startDate = 1;

    if (!unitListOf.includes(unit)) {
      throw new Error('unit参数错误');
    }

    switch (unit) {
    case 'y':
    case 'year':
      this.hdDate.setMonth(startValue);
      this.hdDate.setDate(startDate);
      this.setTimeStart(); // 设置时分秒毫秒为0
      break;
    case 'M':
    case 'month':
      this.hdDate.setDate(startDate);
      this.setTimeStart();
      break;
    case 'd':
    case 'date':
      this.setTimeStart();
      break;
    case 'H':
    case 'hour':
      this.hdDate.setMinutes(startValue);
      this.hdDate.setSeconds(startValue);
      this.hdDate.setMilliseconds(startValue);
      break;
    case 'm':
    case 'minute':
      this.hdDate.setSeconds(startValue);
      this.hdDate.setMilliseconds(startValue);
      break;
    case 's':
    case 'second':
      this.hdDate.setMilliseconds(startValue);
      break;
    case 'w':
    case 'week': {
      const day = this.getDay(false);
      const date = this.getDate();
      const size = date - day;

      this.hdDate.setDate(size);
      this.setTimeStart();
      break;
    }
    case 'wm':
    case 'weekMonday': {
      const day = this.getDay(false);
      const date = this.getDate();
      const size = date - day + 1;

      this.hdDate.setDate(size);
      this.setTimeStart();
      break;
    }
    case 'Q':
    case 'quarter': {
      const currMonth = this.getMonth();

      if (currMonth > 9) {
        this.hdDate.setMonth(9);
      } else if (currMonth > 6) {
        this.hdDate.setMonth(6);
      } else if (currMonth > 3) {
        this.hdDate.setMonth(3);
      } else {
        this.hdDate.setMonth(startValue);
      }
      this.hdDate.setDate(startDate);
      this.setTimeStart();
      break;
    }
    default:
      return this;
    }

    return this;
  }

  /**
   * @desc 设置指定类别值
   * @param { String } unit 单位
   */

  setEndOf(unit = 'year') {
    const unitListOf = [
      ...unitList,
      'quarter', 'Q',
      'week', 'w',
      'weekMonday', 'wm'
    ];
    const endMonth = 11; // 最后一月
    const endMinSec = 59; // 最后分秒
    const endMilliseconds = 999; // 最后毫秒
    const endDate = this.getMonthCountDay(); // 当月最后一天

    if (!unitListOf.includes(unit)) {
      throw new Error('unit参数错误');
    }

    switch (unit) {
    case 'y':
    case 'year':
      this.hdDate.setMonth(endMonth);
      this.hdDate.setDate(endDate);
      this.setTimeEnd(); // 设置时间为结束时间23:59:59 999
      break;
    case 'M':
    case 'month':
      this.hdDate.setDate(endDate);
      this.setTimeEnd();
      break;
    case 'd':
    case 'date':
      this.setTimeEnd();
      break;
    case 'H':
    case 'hour':
      this.hdDate.setMinutes(endMinSec);
      this.hdDate.setSeconds(endMinSec);
      this.hdDate.setMilliseconds(endMilliseconds);
      break;
    case 'm':
    case 'minute':
      this.hdDate.setSeconds(endMinSec);
      this.hdDate.setMilliseconds(endMilliseconds);
      break;
    case 's':
    case 'second':
      this.hdDate.setMilliseconds(endMilliseconds);
      break;
    case 'w':
    case 'week': {
      const day = this.getDay(false);
      const date = this.getDate();
      const size = date - day + 6;

      this.hdDate.setDate(size);
      this.setTimeEnd();
      break;
    }
    case 'wm':
    case 'weekMonday': {
      const day = this.getDay(false);
      const date = this.getDate();
      const size = date - day + 7;

      this.hdDate.setDate(size);
      this.setTimeEnd();
      break;
    }
    case 'Q':
    case 'quarter': {
      const currMonth = this.getMonth();

      if (currMonth > 9) {
        this.hdDate.setMonth(11);
      } else if (currMonth > 6) {
        this.hdDate.setMonth(8);
      } else if (currMonth > 3) {
        this.hdDate.setMonth(5);
      } else {
        this.hdDate.setMonth(2);
      }
      this.hdDate.setDate(endDate);
      this.setTimeEnd();
      break;
    }
    default:
      return this;
    }

    return this;
  }

  // 设置时间为开始
  setTimeStart() {
    this.hdDate.setHours(0);
    this.hdDate.setMinutes(0);
    this.hdDate.setSeconds(0);
    this.hdDate.setMilliseconds(0);
  }
  // 设置时间为结束
  setTimeEnd() {
    this.hdDate.setHours(23);
    this.hdDate.setMinutes(59);
    this.hdDate.setSeconds(59);
    this.hdDate.setMilliseconds(999);
  }

  // 公共方法
  /**
   *  @desc 判断主参数是否正常
   *  @param { Any } data 返回数据
   */
  isParamsError(data) {
    return this.errStatus ? this.message : data;
  }
  // 私有方法

  /**
   * @desc 通用参数说明
   * @param {*} timestamp 传入相减的时间戳 毫秒
   */
  // 返回天数
  getSubDate(timestamp) {
    return Math.floor(timestamp / 1000 / 60 / 60 / 24);
  }

  // 返回小时
  getSubHours(timestamp) {
    return Math.floor(timestamp / 1000 / 60 / 60);
  }

  // 返回分钟
  getSubMinutes(timestamp) {
    return Math.floor(timestamp / 1000 / 60 % 60);
  }

  // 返回秒
  getSubSeconds(timestamp) {
    return Math.floor(timestamp / 1000 % 60);
  }

  /**
   * @desc 将目标字符串替换成数据
   * @param { Boolean } isFillZero 是否补零
   * @param { String } format 格式
   */
  getTimeStr(format, isFillZero, source) {
    let newStr = format;
    let resDataList = {};

    // 替换值列表
    if (source === 'Date') {
      resDataList = {
        yy: this.getYear(isFillZero),
        MM: this.getMonth(isFillZero),
        dd: this.getDate(isFillZero)
      };
    } else if (source === 'Time') {
      resDataList = {
        HH: this.getHours(isFillZero),
        hh: this.getHours(isFillZero, false),
        mm: this.getMinutes(isFillZero),
        ss: this.getSeconds(isFillZero),
        ww: this.getDay()
      };
    } else {
      resDataList = {
        yy: this.getYear(isFillZero),
        MM: this.getMonth(isFillZero),
        dd: this.getDate(isFillZero),
        HH: this.getHours(isFillZero),
        hh: this.getHours(isFillZero, false),
        mm: this.getMinutes(isFillZero),
        ss: this.getSeconds(isFillZero),
        ww: this.getDay(),
        ms: this.getMilliseconds()
      };
    }
    // 定义替换正则
    const regObj = {
      yy: /yy/g,
      MM: /MM/g,
      dd: /dd/g,
      HH: /HH/g,
      hh: /hh/g,
      mm: /mm/g,
      ss: /ss/g,
      ww: /ww/g,
      ms: /ms/g
    };

    Object.keys(resDataList).forEach(item => {
      if (format.includes(item)) {
        newStr = newStr.replace(regObj[item], resDataList[item]);
      }
    });

    return newStr;
  }

  /**
   * @Desc 检查日期参数是否合法
   * @param {String | Number} date  时间
   */
  isValidDate(date) {
    // 时间戳格式
    let newDete = '';

    if (typeof date == 'number') {
      const dateLength = String(date).length;

      if (dateLength === 13 || dateLength === 10) {
        if (dateLength === 10) {
          date = date * 10;
        }
        newDete = new Date(date);
      }
    } else {
      try {
        let setDate = date;

        if (date.includes('-')) {
          setDate = date.replace(/-/g, '/');
        }
        newDete = new Date(setDate);
      } catch (e) {
        return false;
      }
    }

    return newDete instanceof Date && !isNaN(newDete.getTime());
  }

  /**
   * @desc 根据参数转换成统一参数
   * @param { Number | String} date 日期参数
   */
  getParamDate(date) {
    let resData = '';

    // 时间戳格式
    if (typeof date == 'number') {
      const dateLength = String(date).length;

      if (dateLength === 10 || dateLength === 13) {
        if (dateLength === 10) {
          date = date * 1000;
        }
        resData = new Date(date);
      }
      // 字符串格式
    } else {
      resData = new Date(date.replace(/-/g, '/'));
    }

    return resData;
  }

  // 获取当前时间信息
  getLocalTimeInfo(dateObj) {
    let localDate = new Date();

    if (dateObj) {
      localDate = dateObj;
    }

    return {
      yy: localDate.getFullYear(),
      MM: localDate.getMonth() + 1,
      dd: localDate.getDate(),
      hh: localDate.getHours(),
      mm: localDate.getMinutes(),
      ss: localDate.getSeconds(),
      week: localDate.getDay(),
      timestamp: localDate.getTime()
    };
  }
}
module.exports = Wdate;

// 使用说明文档：https://www.npmjs.com/package/wh-date